/*
Copyright 2008 Ian Thomas, Gábor Szuromi

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.wildwinter.anthx;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.ExecTask;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

import java.net.URLEncoder;
import java.io.*;
import java.util.*;

/** Generic haXe target */
public class Haxe extends Task 
{
	protected Commandline _cmdl = new Commandline();
	protected File _dir;
    protected String _main;
    protected Vector<Lib> _libs = new Vector<Lib>();
    protected Path _classPath;
	protected String _dependencyTarget;

    protected boolean _shouldExecute;
    protected File _cacheFolder;
    protected File _dependencyTargetFile;
    protected File _depFile;
    protected File _depTempFile;
    protected File _errTempFile;
	        
	private String _hxml;
	protected boolean _forceRebuild;

	
	public Haxe()
	{
		_shouldExecute = true;
		_forceRebuild=false;
	}

    /******************* Attributes ****************************/
	public void setForceRebuild(Boolean value)
	{
		_forceRebuild=value;
	}
	
    /** Working folder */
	public void setDir(File value) 
	{
		_dir = value;
	}
	
	/** Main entrypoint class */
	public void setMain(String value)
	{
		_main=value;
	}
	
	/** HXML */
	public void setHxml(String value)
	{
		_hxml=value;
	}
	
	/** Classpath - mimic java task functionality */
	public void setClasspath(Path s) 
	{
        createClasspath().append(s);
    }
    
	/** Classpath - mimic java task functionality */
	public void setClasspathref(Path s) 
	{
        createClasspath().append(s);
    }
    
    /** Classpathref - mimic java task functionality */
    public void setClasspathRef(Reference r) 
    {
        createClasspath().setRefid(r);
    }

    public void setIf(String p) 
	{
		_shouldExecute = (getProject().getProperty(p) != null);
    }
    
    public void setUnless(String p) 
	{
		_shouldExecute = (getProject().getProperty(p) == null);
    }

    public void setCacheFolder(File f) 
	{
		_cacheFolder = f;
    }
	
    /******************* Child Elements ************************/
    public Commandline.Argument createArg() 
    {
        return _cmdl.createArgument();
    }
    
    public Lib createLib() 
    {
        Lib lib = new Lib();
        _libs.add(lib);
        return lib;
    }
    
    public Path createClasspath() 
    {
	    if (_classPath==null)
	    	_classPath=new Path(getProject());
	    return _classPath;
    }

	/***********************************************************/
    protected void setDependencyTarget(String f) 
	{
		_dependencyTarget = f;
    }
	
    public void execute() 
    {
		if (_cacheFolder==null)
		{
			_cacheFolder=new File(System.getProperty("java.io.tmpdir"));
		}
		
		try 
		{
			if (_dir == null)
				_dir = getProject().getBaseDir();
	    
			ExecTask haxe=HaxeEnv.createHaxeTask(getProject());

			if(_dependencyTarget != null) 
			{
				_dependencyTargetFile=expandFile(_dependencyTarget);
				
				_depFile = new File(_cacheFolder, hashFileName(_dependencyTargetFile) + ".deps");
          
			    if(!_forceRebuild&&isUpToDate())
					return;

				// Collect dependencies into a temp file
				String depName;
				if (_main!=null)
					depName=_main;
				else
					depName=_dir.getName();
					
				try 
				{
					_depTempFile = File.createTempFile(depName, ".deps");
					_errTempFile = File.createTempFile(depName, ".err");
				} 
				catch(IOException e) 
				{
					throw new BuildException("Unable to create temporary files for dependency tracking!");
				}
				haxe.setOutput(_depTempFile);
				haxe.setError(_errTempFile);
			}  
	    
			// Working Directory
			haxe.setDir(_dir);

			if (_hxml!=null)
			{
				File file=expandFile(_hxml);
				if (!file.exists())
					throw new BuildException("Can't find hxml file:"+file,getLocation());
				haxe.createArg().setFile(file);
				
				getProject().log("haxe task - hxml set, ignoring other options.", Project.MSG_DEBUG);
			
				haxe.execute();
				return;
			}
		
			if (_main!=null)
				haxe.createArg().setLine("-main "+_main);
		
			// Add libs
			for (Enumeration e = _libs.elements(); e.hasMoreElements();)
			{
				Lib lib = (Lib) e.nextElement();
				haxe.createArg().setLine("-lib "+lib.format());
			}
        
			// Classpath
			if (_classPath!=null)
			{
				String paths[]=_classPath.list();
				for (int i=0;i<paths.length;i++)
				{
					haxe.createArg().setValue("-cp");
					haxe.createArg().setValue(paths[i]);
				}
			}
        
			// Give a derived task a chance
			addOptions(haxe);

			// Needed for dependency tracking
			haxe.createArg().setValue("-v");
		
			// Add any arguments
			String args[]=_cmdl.getArguments();
			for (int i = 0; i < args.length; i++) 
			{
				haxe.createArg().setValue(args[i]);
			}

			haxe.execute();
        
			postExec(haxe);
      
			if(_dependencyTargetFile != null) 
			{
				try 
				{
					LineNumberReader     in = new LineNumberReader(new FileReader(_depTempFile));
					FileWriter           out = new FileWriter(_depFile);
					String               line;

					while((line = in.readLine()) != null) 
					{
						if(!line.startsWith("Parsed"))
							continue;

						out.write(line.substring(7) + "\n");
					}

					in.close();
					out.close();
				} 
				catch(IOException e) 
				{
					throw new BuildException(e.toString());
				}
			}
		} 
		catch(BuildException e) 
		{
			throw e;
		} 
		finally 
		{
			printError();
		 
			if(_depTempFile != null)
				_depTempFile.delete();
			if(_errTempFile != null)
				_errTempFile.delete();
		}
    }
    
    protected void addOptions(ExecTask haxe)
    {
    }
    
    protected void postExec(ExecTask haxe) 
	{
    }
    
    protected File expandFile(String file)
    {
		File dest = new File(file);
		if(dest.isAbsolute())
			return dest;

		return new File(_dir, file);
    }

    protected boolean isUpToDate() 
	{
		LineNumberReader in = null;
		try 
		{
			in = new LineNumberReader(new FileReader(_depFile));
		} 
		catch(IOException e) 
		{
			return false;
		}
		 
		try 
		{
			String line;

			while((line = in.readLine()) != null) 
			{
				if(SelectorUtils.isOutOfDate(expandFile(line), _dependencyTargetFile, 0)) 
				{
					in.close();
					return false;
				}
			}
		 
			in.close();
		} 
		catch(IOException e) 
		{
			return false;
		}
		return true;
    }

    protected void printError() 
	{
		if(_errTempFile == null)
			return;

		try 
		{
			LineNumberReader in = new LineNumberReader(new FileReader(_errTempFile));
			String line;

			while((line = in.readLine()) != null) 
			{
				System.err.println(line);
			}

			in.close();
		} 
		catch(IOException e) 
		{
		}
    }
	
	protected String hashFileName(File file) throws BuildException
	{
		String name=file.getPath();
		String hash=name.hashCode()+file.getName();
		try
		{
			return URLEncoder.encode(hash,"UTF-8");
		}
		catch (Exception e)
		{
			throw new BuildException("Can't encode string:"+hash,e);
		}
	}
    
    /***********************************************************/
    public class Lib
    {
	    private String _name;
	    private String _version;

        public void setName(String value)
        { 
	        _name = value;
	    }
	    
        public void setVersion(String value)
        { 
	        _version = value;
	    }
	    
	    public String format() throws BuildException
	    {
		    if (_name==null)
		    	throw new BuildException("No name specified for haxe lib.");
		    if (_version!=null)
		    	return _name+":"+_version;
		    return _name;
	    }
    }
}
